#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>
#include <functional>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
const int num = 5;
int channel_number = 1; // 管道的编号，之后构成管道的名称
typedef function<void()> task;

// 任务
void Play_Music() {
    cout << "子进程" << getpid() << "正在执行播放音乐的任务……" << endl;
}
void Play_Video() {
    cout << "子进程" << getpid() << "正在执行播放视频的任务……" << endl;
}

void Print_Log() {
    cout << "子进程" << getpid() << "正在执行打印日志的任务……" << endl;    
}

void Download() {
    cout << "子进程" << getpid() << "正在执行下载任务……" << endl;    
}

// channel结构体类型
struct channel {
    // 构造函数
    channel(int fd, pid_t id) 
        :ctrfd(fd)
        ,workid(id)
    {
        name = "channel-" + to_string(channel_number++);
    }

    int ctrfd; // 控制该管道的文件描述符
    pid_t workid; // 链接该管道的子进程id
    string name; // 管道的名称
};


int main() {
    vector<channel> channels; // 管道数组
    vector<task> tasks; // 任务数组
    // 初始化任务数组
    tasks.push_back(Play_Music);
    tasks.push_back(Play_Video);
    tasks.push_back(Print_Log);
    tasks.push_back(Download);
    
    vector<pid_t> oldfd; // 保存旧的文件描述符
    // 创建进程，创建管道
    for (int i = 0; i < num; i++) {
        // 创建管道
        int pipefd[2] = { 0 };
        int res = pipe(pipefd);
        assert(res == 0);

        // 创建进程
        pid_t id = fork();
        assert(id != -1);

        if (id == 0) {
            // 先关闭所有旧的文件描述符
            if (!oldfd.empty()) {
                for (auto fd : oldfd) {
                    close(fd);
                }
            }

            // 子进程
            close(pipefd[1]); // 子进程读，子进程关闭写端
            while (true) {
                int index = 0;
                int n = read(pipefd[0], &index, sizeof(index));
                if (n == sizeof(index)) {
                    // 读取成功，执行任务
                    tasks[index]();
                } else if (n == 0) {
                    // 说明写端已经关闭，则子进程停止
                    break;
                }
            }
            exit(0); // 让子进程执行完就退出
        }

        // 父进程
        close(pipefd[0]); // 父进程写，父进程关闭读端
        channels.push_back(channel(pipefd[1], id));
        oldfd.push_back(pipefd[1]);
    }

    // 种一颗随机数种子
    srand(time(NULL) * getpid());
    const int T = 10; // 让父进程选择T次

    for (int i = 0; i < T; i++) {
        int channeli = i % num; // 管道下标
        int taski = rand() % tasks.size(); // 任务下标
        channel& c = channels[channeli];
        // 向管道发送下标
        write(c.ctrfd, &taski, sizeof(taski));
        sleep(1);
    }

    // 关闭管道，回收子进程

    // 方案一
    // for (auto& c : channels) {
    //     close(c.ctrfd);
    //     cout << "close " << c.name << " sucess……" << endl;
    // }
    // for (auto& c : channels) {
    //     pid_t rid = waitpid(c.workid, nullptr, 0);
    //     if (rid == c.workid) {
    //         cout << "wait child process:" << rid << " sucess……" << endl;
    //     }
    // }

    // // 方案二
    // for (int i = channels.size() - 1; i >= 0; i--) {
    //     close(channels[i].ctrfd);
    //     cout << "close " << channels[i].name << " sucess……" << endl;
    //     pid_t rid = waitpid(channels[i].workid, nullptr, 0);
    //     if (rid == channels[i].workid) {
    //         cout << "wait child process:" << rid << " sucess……" << endl;
    //     }
    // }

    // 方案三(配合oldfd)
    for (auto& c : channels) {
        close(c.ctrfd);
        cout << "close " << c.name << " sucess……" << endl;
        pid_t rid = waitpid(c.workid, nullptr, 0);
        if (rid == c.workid) {
            cout << "wait child process:" << rid << " sucess……" << endl;
        }        
    }
    return 0;
}